From e437a9c348b795445e1dadc801497c1075c3738e Mon Sep 17 00:00:00 2001 From: Benjamin Otte Date: Thu, 9 Jun 2022 07:14:30 +0200 Subject: [PATCH] inscription: Add a11y support for text interface This is entirely untested. --- gtk/a11y/gtkatspitext.c | 271 ++++++++++++++++++++++++++++++++++++ gtk/gtkinscription.c | 11 +- gtk/gtkinscriptionprivate.h | 33 +++++ 3 files changed, 314 insertions(+), 1 deletion(-) create mode 100644 gtk/gtkinscriptionprivate.h diff --git a/gtk/a11y/gtkatspitext.c b/gtk/a11y/gtkatspitext.c index e4a6140298..e7bd9275be 100644 --- a/gtk/a11y/gtkatspitext.c +++ b/gtk/a11y/gtkatspitext.c @@ -32,6 +32,7 @@ #include "gtkatcontextprivate.h" #include "gtkdebug.h" #include "gtkeditable.h" +#include "gtkinscriptionprivate.h" #include "gtklabelprivate.h" #include "gtkentryprivate.h" #include "gtksearchentryprivate.h" @@ -406,6 +407,274 @@ static const GDBusInterfaceVTable label_vtable = { NULL, }; +/* }}} */ +/* {{{ GtkInscription */ + +static void +inscription_handle_method (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + GtkATContext *self = user_data; + GtkAccessible *accessible = gtk_at_context_get_accessible (self); + GtkWidget *widget = GTK_WIDGET (accessible); + + if (g_strcmp0 (method_name, "GetCaretOffset") == 0) + { + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(i)", 0)); + } + else if (g_strcmp0 (method_name, "SetCaretOffset") == 0) + { + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", FALSE)); + } + else if (g_strcmp0 (method_name, "GetText") == 0) + { + int start, end; + const char *text; + int len; + char *string; + + g_variant_get (parameters, "(ii)", &start, &end); + + text = gtk_inscription_get_text (GTK_INSCRIPTION (widget)); + len = g_utf8_strlen (text, -1); + + start = CLAMP (start, 0, len); + end = CLAMP (end, 0, len); + + if (end <= start) + string = g_strdup (""); + else + { + const char *p, *q; + p = g_utf8_offset_to_pointer (text, start); + q = g_utf8_offset_to_pointer (text, end); + string = g_strndup (p, q - p); + } + + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", string)); + g_free (string); + } + else if (g_strcmp0 (method_name, "GetTextBeforeOffset") == 0) + { + PangoLayout *layout = gtk_inscription_get_layout (GTK_INSCRIPTION (widget));; + int offset; + AtspiTextBoundaryType boundary_type; + char *string; + int start, end; + + g_variant_get (parameters, "(iu)", &offset, &boundary_type); + + string = gtk_pango_get_text_before (layout, offset, boundary_type, &start, &end); + + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(sii)", string, start, end)); + g_free (string); + } + else if (g_strcmp0 (method_name, "GetTextAtOffset") == 0) + { + PangoLayout *layout = gtk_inscription_get_layout (GTK_INSCRIPTION (widget));; + int offset; + AtspiTextBoundaryType boundary_type; + char *string; + int start, end; + + g_variant_get (parameters, "(iu)", &offset, &boundary_type); + + string = gtk_pango_get_text_at (layout, offset, boundary_type, &start, &end); + + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(sii)", string, start, end)); + g_free (string); + } + else if (g_strcmp0 (method_name, "GetTextAfterOffset") == 0) + { + PangoLayout *layout = gtk_inscription_get_layout (GTK_INSCRIPTION (widget));; + int offset; + AtspiTextBoundaryType boundary_type; + char *string; + int start, end; + + g_variant_get (parameters, "(iu)", &offset, &boundary_type); + + string = gtk_pango_get_text_after (layout, offset, boundary_type, &start, &end); + + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(sii)", string, start, end)); + g_free (string); + } + else if (g_strcmp0 (method_name, "GetCharacterAtOffset") == 0) + { + int offset; + const char *text; + gunichar ch = 0; + + g_variant_get (parameters, "(i)", &offset); + + text = gtk_inscription_get_text (GTK_INSCRIPTION (widget)); + + if (0 <= offset && offset < g_utf8_strlen (text, -1)) + ch = g_utf8_get_char (g_utf8_offset_to_pointer (text, offset)); + + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(i)", ch)); + } + else if (g_strcmp0 (method_name, "GetStringAtOffset") == 0) + { + PangoLayout *layout = gtk_inscription_get_layout (GTK_INSCRIPTION (widget));; + int offset; + AtspiTextGranularity granularity; + char *string; + int start, end; + + g_variant_get (parameters, "(iu)", &offset, &granularity); + + string = gtk_pango_get_string_at (layout, offset, granularity, &start, &end); + + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(sii)", string, start, end)); + g_free (string); + } + else if (g_strcmp0 (method_name, "GetAttributes") == 0) + { + PangoLayout *layout = gtk_inscription_get_layout (GTK_INSCRIPTION (widget));; + GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{ss}")); + int offset; + int start, end; + + g_variant_get (parameters, "(i)", &offset); + + gtk_pango_get_run_attributes (layout, &builder, offset, &start, &end); + + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(a{ss}ii)", &builder, start, end)); + } + else if (g_strcmp0 (method_name, "GetAttributeValue") == 0) + { + PangoLayout *layout = gtk_inscription_get_layout (GTK_INSCRIPTION (widget));; + GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{ss}")); + int offset; + const char *name; + int start, end; + GVariant *attrs; + const char *val; + + g_variant_get (parameters, "(i&s)", &offset, &name); + + gtk_pango_get_run_attributes (layout, &builder, offset, &start, &end); + + attrs = g_variant_builder_end (&builder); + if (!g_variant_lookup (attrs, name, "&s", &val)) + val = ""; + + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(s)", val)); + g_variant_unref (attrs); + } + else if (g_strcmp0 (method_name, "GetAttributeRun") == 0) + { + PangoLayout *layout = gtk_inscription_get_layout (GTK_INSCRIPTION (widget));; + GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{ss}")); + int offset; + gboolean include_defaults; + int start, end; + + g_variant_get (parameters, "(ib)", &offset, &include_defaults); + + if (include_defaults) + gtk_pango_get_default_attributes (layout, &builder); + + gtk_pango_get_run_attributes (layout, &builder, offset, &start, &end); + + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(a{ss}ii)", &builder, start, end)); + } + else if (g_strcmp0 (method_name, "GetDefaultAttributes") == 0 || + g_strcmp0 (method_name, "GetDefaultAttributeSet") == 0) + { + PangoLayout *layout = gtk_inscription_get_layout (GTK_INSCRIPTION (widget));; + GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE ("a{ss}")); + + gtk_pango_get_default_attributes (layout, &builder); + + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(a{ss})", &builder)); + } + else if (g_strcmp0 (method_name, "GetNSelections") == 0) + { + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(i)", 0)); + } + else if (g_strcmp0 (method_name, "GetSelection") == 0) + { + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "No selections available"); + } + else if (g_strcmp0 (method_name, "AddSelection") == 0) + { + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", FALSE)); + } + else if (g_strcmp0 (method_name, "RemoveSelection") == 0) + { + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", FALSE)); + } + else if (g_strcmp0 (method_name, "SetSelection") == 0) + { + g_dbus_method_invocation_return_value (invocation, g_variant_new ("(b)", FALSE)); + } + else if (g_strcmp0 (method_name, "GetCharacterExtents") == 0) + { + g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, ""); + } + else if (g_strcmp0 (method_name, "GetRangeExtents") == 0) + { + g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, ""); + } + else if (g_strcmp0 (method_name, "GetBoundedRanges") == 0) + { + g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, ""); + } + else if (g_strcmp0 (method_name, "ScrollSubstringTo") == 0) + { + g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, ""); + } + else if (g_strcmp0 (method_name, "ScrollSubstringToPoint") == 0) + { + g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, ""); + } +} + +static GVariant * +inscription_get_property (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *property_name, + GError **error, + gpointer user_data) +{ + GtkATContext *self = user_data; + GtkAccessible *accessible = gtk_at_context_get_accessible (self); + GtkWidget *widget = GTK_WIDGET (accessible); + + if (g_strcmp0 (property_name, "CharacterCount") == 0) + { + const char *text; + int len; + + text = gtk_inscription_get_text (GTK_INSCRIPTION (widget)); + len = g_utf8_strlen (text, -1); + + return g_variant_new_int32 (len); + } + else if (g_strcmp0 (property_name, "CaretOffset") == 0) + { + return g_variant_new_int32 (0); + } + + return NULL; +} + +static const GDBusInterfaceVTable inscription_vtable = { + inscription_handle_method, + inscription_get_property, + NULL, +}; + /* }}} */ /* {{{ GtkEditable */ @@ -1301,6 +1570,8 @@ gtk_atspi_get_text_vtable (GtkAccessible *accessible) { if (GTK_IS_LABEL (accessible)) return &label_vtable; + else if (GTK_IS_INSCRIPTION (accessible)) + return &inscription_vtable; else if (GTK_IS_EDITABLE (accessible) && GTK_IS_TEXT (gtk_editable_get_delegate (GTK_EDITABLE (accessible)))) return &editable_vtable; diff --git a/gtk/gtkinscription.c b/gtk/gtkinscription.c index 522d943933..ffe7ffd09c 100644 --- a/gtk/gtkinscription.c +++ b/gtk/gtkinscription.c @@ -19,7 +19,7 @@ #include "config.h" -#include "gtkinscription.h" +#include "gtkinscriptionprivate.h" #include "gtkcssnodeprivate.h" #include "gtkcssstylechangeprivate.h" @@ -653,6 +653,8 @@ gtk_inscription_class_init (GtkInscriptionClass *klass) G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (gobject_class, N_PROPS, properties); + + gtk_widget_class_set_accessible_role (widget_class, GTK_ACCESSIBLE_ROLE_LABEL); } static void @@ -669,6 +671,13 @@ gtk_inscription_init (GtkInscription *self) pango_layout_set_wrap (self->layout, PANGO_WRAP_WORD_CHAR); } +/* for a11y */ +PangoLayout * +gtk_inscription_get_layout (GtkInscription *self) +{ + return self->layout; +} + /** * gtk_inscription_new: * @text: (nullable): The text to display. diff --git a/gtk/gtkinscriptionprivate.h b/gtk/gtkinscriptionprivate.h new file mode 100644 index 0000000000..97de354e27 --- /dev/null +++ b/gtk/gtkinscriptionprivate.h @@ -0,0 +1,33 @@ +/* + * Copyright © 2022 Benjamin Otte + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . + * + * Authors: Benjamin Otte + */ + +#ifndef __GTK_INSCRIPTION_PRIVATE_H__ +#define __GTK_INSCRIPTION_PRIVATE_H__ + + +#include + + +G_BEGIN_DECLS + +PangoLayout * gtk_inscription_get_layout (GtkInscription *self); + +G_END_DECLS + +#endif /* __GTK_INSCRIPTION_PRIVATE_H__ */ -- 2.30.2